package wscore

import (
	"air_chat/common/global"
	"context"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// 消息
// {"from_user_id": 1, "to_user_id": 2,"msg_type": 1, "media_type": 1 ,"content":"hello"}
type AirMessage struct {
	FromUserId int64  `json:"from_user_id" gorm:"column:from_user_id"` // 发送者
	ToUserId   int64  `json:"to_user_id" gorm:"column:to_user_id"`     // 接收者
	MsgType    int    `json:"msg_type" `                               //  消息类型   1-单聊，2-群聊
	MediaType  int    `json:"media_type"`                              // 媒体类型 1-文字，2-图片，3-语音，4-视频
	Content    string `json:"content"`                                 // 消息内容  其实可以把图片，音频，视频的地址保存在 content 中，然后根据 media_type 来判断                             //  消息类型   1-单聊，2-群聊
	MsgSize    int64  `json:"msg_size"`                                // 消息大小
}

// 节点
type ImNode struct {
	UserId    int64
	Conn      *websocket.Conn
	DataQueue chan []byte
	GroupSets map[int64]struct{} // 群组集合，使用map实现set功能
}

// 在线用户映射关系
var onlineMap map[int64]*ImNode = make(map[int64]*ImNode, 0)

// 读写锁
var rwLocker sync.RWMutex

var udpSendChan chan []byte = make(chan []byte, 1024)

const (
	REDIS_SUB_PUSh = 1 + iota
	UDP_TRANS
	ASYNQ_TRANS
)

var transRouter = UDP_TRANS

// 防止跨域站点伪造请求
var upgrade = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		/* userId := r.Header.Get("user_id")
		if(userId == "") {
			return false
		} */
		return true
	},
}

func AirChat(w http.ResponseWriter, r *http.Request) {

	// 升级为websocket协议
	conn, err := upgrade.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("升级为websocket失败：", err)
		return
	}

	// 获取连接的用户信息
	userId, _ := strconv.ParseInt(r.Header.Get("user_id"), 10, 64)
	// query := r.URL.Query()
	// userId, _ := strconv.ParseInt(query.Get("user_id"), 10, 64)
	// 初始化节点
	node := &ImNode{
		UserId:    userId,
		Conn:      conn,
		DataQueue: make(chan []byte, 50),
		GroupSets: make(map[int64]struct{}),
	}

	// 用户上线，将用户加入到在线用户映射关系中
	userOnline(node)

	fmt.Printf("user_id=%d, all online=%v\n", userId, onlineMap)

	// 发送逻辑 给连接上来的用户响应消息
	go sendProc(node)

	// 接收逻辑 接收连接上来的用户消息
	go recvProc(node)

	sendMsg(userId, "欢迎来到聊天室")

	// 心跳检测
	go heartBeat(node)
}

// 心跳检测
func heartBeat(node *ImNode) {
	for {
		_, _, err := node.Conn.ReadMessage()
		if err != nil {
			// 编写患者断开连接逻辑
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				fmt.Println("用户断开连接")
				// 编写患者断开连接逻辑
				offlineUser(node.UserId)
			}
		}
		time.Sleep(5 * time.Second)
	}
}

// 发送逻辑 给连接上来的用户响应消息
func sendProc(node *ImNode) {
	for {
		select {
		// 从通道中获取数据
		case data := <-node.DataQueue:
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				// 编写患者断开连接逻辑
				if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
					fmt.Println("用户断开连接")
					// 编写患者断开连接逻辑
					offlineUser(node.UserId)
				}
				return
			}
		}
	}
}

// 完成接收逻辑 接收连接上来的用户消息
func recvProc(node *ImNode) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			// 编写患者断开连接逻辑
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				fmt.Println("用户断开连接")
				// 编写患者断开连接逻辑
				offlineUser(node.UserId)
			}
			return
		}

		fmt.Printf("recv<=%s\n", data)
		switch transRouter {
		case REDIS_SUB_PUSh:
			// 将消息推送到redis里面
			pubRedisMsg(string(data))
		case UDP_TRANS:
			broadMsg(data)
		}
	}
}

func broadMsg(data []byte) {
	udpSendChan <- data
}

// 完成udp数据发送协程
func udpSendProc() {
	conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(192, 168, 3, 255),
		Port: 3000,
	})
	defer conn.Close()
	if err != nil {
		fmt.Println(err)
	}
	for {
		select {
		case data := <-udpSendChan:
			_, err := conn.Write(data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

// 完成udp数据接收协程
func udpRecvProc() {
	conn, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: 3000,
	})
	if err != nil {
		fmt.Println("UDP监听失败:", err)
		return
	}
	defer conn.Close()

	for {
		var buf [1024]byte
		n, err := conn.Read(buf[0:])
		if err != nil {
			fmt.Println("read udp failed, err:", err)
			continue
		}
		// 确保我们有有效的数据再进行处理
		if n > 0 {
			dispatch(buf[0:n])
		}
	}
}

// 发布redis消息
func pubRedisMsg(message string) {
	// 使用 global.Publish 发布消息
	err := global.Publish(context.Background(), global.PublishKey, message)
	if err != nil {
		fmt.Println("发布消息失败：", err)
	}
}

func init() {
	switch transRouter {
	case REDIS_SUB_PUSh:
		go subRedisMsg()
	case UDP_TRANS:
		go udpSendProc()
		go udpRecvProc()
	case ASYNQ_TRANS:
		// TODO
	default:
		panic("transRouter参数错误")
	}
}

// 订阅redis消息
func subRedisMsg() {
	for {
		// 使用 global.Subscribe 订阅消息
		message, err := global.Subscribe(context.Background(), global.PublishKey)
		if err != nil {
			fmt.Println("订阅消息失败：", err)
			time.Sleep(time.Second) // 发生错误时等待一秒再重试
			continue
		}
		fmt.Printf("redis message <=%s\n", message)
		// 解析消息
		dispatch([]byte(message))
	}
}

// 后端调度逻辑处理
func dispatch(data []byte) {
	// 添加错误处理和日志记录
	if len(data) == 0 {
		fmt.Println("收到空数据，忽略处理")
		return
	}

	message := &AirMessage{}
	err := json.Unmarshal(data, &message)
	if err != nil {
		// 打印接收到的数据，帮助调试
		fmt.Printf("接收到的数据: %s。json解码失败, err: %v\n", string(data), err)
		return
	}

	// 确保ToUserId有效
	if message.ToUserId <= 0 {
		fmt.Println("无效的接收者ID:", message.ToUserId)
		return
	}

	switch message.MsgType {
	case 1: // 私信
		sendMsg(message.ToUserId, string(data))
	// case 2: // 群发
	// 	sendGroupMsg(message.FromUserId, message.MsgType, message.Content)
	// case 3: // 广播
	// 	sendAllMsg(message.MsgType, message.Content)
	// case 4: // 系统消息
	// 	sendSysMsg(message.MsgType, message.Content)
	default:
		fmt.Println("消息类型错误:", message.MsgType)
	}
}

func sendMsg(toUserId int64, content string) {
	// 获取 toUserId 的 conn
	rwLocker.RLock()
	node, ok := onlineMap[toUserId]
	rwLocker.RUnlock()
	if ok {
		node.DataQueue <- []byte(content)
	} else {
		fmt.Println("用户不存在")
	}
}

// 用户上线
func userOnline(node *ImNode) {
	rwLocker.Lock()
	onlineMap[node.UserId] = node
	rwLocker.Unlock()
}

// 用户下线
func offlineUser(userId int64) {
	rwLocker.Lock()
	delete(onlineMap, userId)
	rwLocker.Unlock()
}
